`timescale 1ns / 1ps
//Name: Robert Smith
//PID: A08609119
//Name: Shreenidhi Chowkwale
//PID: A09089080

module datapath#(parameter I_WIDTH = 17, IA_WIDTH = 10, REG_SIZE = 4, D_WIDTH = 34, PA_WIDTH = 4, DA_WIDTH = 13, FIFO_WIDTH = 68, LDINST_WIDTH = 136, OPCODE_WIDTH = 5)
(
	// Datapath Control Signals
	input clk,
	input reset_i,
   input is_jal_i,
	input is_dmem_i,
	input is_alu_i,
	input [OPCODE_WIDTH - 1 : 0] op_code_i,
	input [REG_SIZE - 1 : 0] wa_i,
	input [REG_SIZE - 1 : 0] ra0_i,
	input [REG_SIZE - 1 : 0] ra1_i,
	input wen_i,
	input ldinst_valid_i,
	input is_computation_imm_i,
	input is_sw_i,
	input is_staj_or_ldrsw_i,
	input is_staj_i,
	input is_in_i,
	input is_gic_i,
	// PC Compute Control Signals
	input is_swtch_i,
	input has_lab_or_imm_i,
	input uses_label_i,
	input is_swtch_or_jr_i,
	// DMEM Control Signals
	input read_write_req_i,
	input write_en_i,
	
	// Instruction Inputs
	input instruction_valid_i,
	input [IA_WIDTH - 1 : 0] instruction_addr_i,
	input [IA_WIDTH - 1 : 0] instruction_label_i,
	input [D_WIDTH - 1 : 0] instruction_imm_i,
	
	input [32 - 1 : 0] instr_count_i,
	
	// Output
	output [IA_WIDTH - 1 : 0] restart_addr_o,
	output refused_o,
	output branch_o,
	
	// IO Devices
	input [D_WIDTH - 1 : 0] in_data_i,
	output [D_WIDTH - 1 : 0] out_data_o,
	output [PA_WIDTH - 1 : 0] in_addr_o,
	output [PA_WIDTH - 1 : 0] out_addr_o,
	
	
	// Timing test outputs
	output [D_WIDTH - 1 : 0] rd0_o,
	output [D_WIDTH - 1 : 0] rd1_o,
	output [D_WIDTH - 1 : 0] wd_o
);



	// ALU outputs
	wire [D_WIDTH - 1 : 0 ] wd, op_reg, rd0, rd1;
	
	// DMEM output
	wire [LDINST_WIDTH - 1 : 0] ldinst_data;
	
	//MUXes
	reg [D_WIDTH - 1 : 0] is_jal_mux, is_dmem_mux, is_alu_mux, is_gic_mux, is_computation_imm_mux, is_sw_mux, is_in_mux, is_staj_mux;
	reg [DA_WIDTH - 1 : 0] is_staj_or_ldrsw_mux;
	
	//Registers
	reg [IA_WIDTH - 1 : 0] instruction_addr_r;
	reg [IA_WIDTH - 1 : 0 ] instruction_label_r;
	reg [D_WIDTH - 1 : 0] instruction_imm_r;
	reg [D_WIDTH - 1 : 0] in_data_r;
	
	// ALU outputs
	wire [D_WIDTH - 1: 0] alu_out;
	wire alu_branch_out;

	reg [IA_WIDTH - 1 : 0] restart_addr_next; 
	reg branch_next;
	
	reg reset_r;

  // Timing test
	assign rd0_o = rd0;
	assign rd1_o = rd1;
	assign wd_o = is_alu_mux;
		
	always_ff @(posedge clk)
		begin
			reset_r <= reset_i;
			if(instruction_valid_i)
				begin
					instruction_addr_r <= instruction_addr_i;
					instruction_label_r <= instruction_label_i;
					instruction_imm_r <= instruction_imm_i;
					in_data_r <= in_data_i;
				end
		end
		
	always_comb
		begin
            if(is_jal_i)
                is_jal_mux = { 2'd0, instruction_addr_r + 4 };
            else
                is_jal_mux = instruction_imm_r;
			if(is_dmem_i)
				is_dmem_mux = ldinst_data[LDINST_WIDTH - 1 : LDINST_WIDTH - D_WIDTH];
			else
				is_dmem_mux = is_jal_mux;
			if(is_alu_i)
				is_alu_mux = alu_out;
			else
				is_alu_mux = is_dmem_mux;
			if(is_gic_i)
				is_gic_mux = instr_count_i;
			else
				is_gic_mux = is_alu_mux;
		end

	regfile register_file
	(
		.clk(clk)
		,.wen_i(wen_i)
		,.instr_i(ldinst_data)
		,.ldinst_valid_i(ldinst_valid_i)
		,.wa_i(wa_i)
		,.wd_i(is_gic_mux)
		,.ra0_i(ra0_i)
		,.ra1_i(ra1_i)
		,.op_reg_o(op_reg)
		,.rd0_o(rd0)
		,.rd1_o(rd1)
	);
	
	always_comb
		begin
			if(is_computation_imm_i)
				is_computation_imm_mux = instruction_imm_r;
			else
				is_computation_imm_mux = rd1;
			if(is_sw_i)
				is_sw_mux = rd1;
			else
				is_sw_mux = rd0;
		end

	alu alu_module
	(
		.op_code(op_code_i)
		,.d0_i(is_sw_mux)
		,.d1_i(is_computation_imm_mux)
		,.op_reg_i(op_reg)
		,.d_o(alu_out)
		,.branch_o(alu_branch_out)
	);
	
	always_comb
		begin
			branch_next = alu_branch_out;
		end
		
	assign branch_o = branch_next;
	
	always_comb
		begin
			if(is_staj_or_ldrsw_i)
				is_staj_or_ldrsw_mux = rd0[DA_WIDTH - 1 : 0];
			else
				is_staj_or_ldrsw_mux = alu_out[DA_WIDTH - 1 : 0];
		end
	always_comb
		begin
			if(is_staj_i)
				is_staj_mux = rd1;
			else
				is_staj_mux = rd0;
		end
		
	always_comb
		begin
			if(is_in_i)
				is_in_mux = in_data_i; //Not sure if it should be from in_data_i or in_data_r
			else
				is_in_mux = is_staj_mux;
		end

	dmem dmem_module
	(
		.reset_i(reset_r)
		,.clk(clk)
		,.read_write_req_i(read_write_req_i)
		,.write_en_i(write_en_i)
		,.addr_i(is_staj_or_ldrsw_mux)
		,.din_i(is_in_mux)
		,.dout_o(ldinst_data)
		,.refused_o(refused_o)
	);
	
	wire [IA_WIDTH - 1 : 0] pc;
    
	pc_compute pc_module
	(
		.regfile_output_i(rd0)
		,.label_i(instruction_label_r)
		,.pc_i(instruction_addr_r)
		,.is_swtch_i(is_swtch_i)
		,.has_lab_or_imm_i(has_lab_or_imm_i)
		,.uses_label_i(uses_label_i)
		,.is_swtch_or_jr_i(is_swtch_or_jr_i)
		,.branch_taken_i(alu_branch_out)
		,.pc_o(pc)
	);
	
	always_comb
		begin
			if(reset_r)
				restart_addr_next = 10'b0;
			else
				restart_addr_next = pc;
		end
	
	assign restart_addr_o = restart_addr_next;
	assign out_data_o = rd0;
	assign in_addr_o = rd1[PA_WIDTH - 1 : 0];
	assign out_addr_o = rd1[PA_WIDTH - 1 : 0];
    
endmodule
